#pragma once

#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <sys/wait.h>
#include <fcntl.h>

#include "../comm/util.hpp"
#include "../comm/log.hpp"

namespace ns_complier
{

    class Complier
    {
    public:
        Complier() {}
        ~Complier() {}

        static bool Compile(const std::string &file_name)
        {
            pid_t id = fork();

            if (id < 0)
            {
                ns_log::lg.LogMessage(ns_log::Fatal, "内部错误,创建子进程失败!\n");
                return false;
            }
            else if (id == 0)
            {
                // child

                umask(0); // 在创建文件时，最好先将 umask 进行清零,以免平台问题，创建的权限不同

                // 先打开 compile_error 文件，如果出现编译错误，那么可以写入文件中，以便后续判断
                // 如果编译出错，获取 g++ 编译的错误信息,                             // 只写
                int _stderr = open(ns_util::PathUtil::Compile_Error(file_name).c_str(), O_CREAT | O_WRONLY, 0644);
                //  _stderr 文件描述符

                if (_stderr < 0)
                {
                    // 打开文件失败 , 返回
                    
                    ns_log::lg.LogMessage(ns_log::Warning, "警告,can`t open [file] Stderr\n");
                    exit(1);
                }

                {
                    // 重定向标准错误，将 g++ 的错误信息打印至 文件
                    dup2(_stderr, 2);
                }

                // 子进程负责编译，使用程序替换编译程序

                // g++ -o target src -std=c+11
                //  int execlp(const char *file, const char *arg, ...
                //        /*, (char *) NULL */);
                //  execlp 第一个参数是执行什么程序， 之后的参数是执行该命令的格式

                // !  这里要添加 -D COMPILER_ONLINE ,使得 tail.cpp 中去掉头文件 header.cpp 否则会报错
                execlp("g++", "g++", "-o", ns_util::PathUtil::Exe(file_name).c_str(),
                       ns_util::PathUtil::Src(file_name).c_str(), "-D","COMPILER_ONLINE", "-std=c++11", nullptr /* 结束以nullptr 结尾 */); // ！！！！！！！！！！！！！！！！！！！！！！！！！！！！！

                // 如果程序运行到此处，说明 g++ 没有成功进行程序替换
                ns_log::lg.LogMessage(ns_log::Error, "g++ 启动失败!\n");
                exit(2);
            }
            else
            {
                // 等待子进程的 id , 等待成功说明子进程编译结束
                waitpid(id, nullptr, 0);

                // 检查子进程是否编译成功，检查是否产生了对应的可执行文件即可
                if (ns_util::FileUtil::IsFileExists(ns_util::PathUtil::Exe(file_name).c_str()))
                {
                    ns_log::lg.LogMessage(ns_log::Info, "%s 编译成功!\n", ns_util::PathUtil::Src(file_name).c_str());
                    return true;
                }
                return false;
            }
            ns_log::lg.LogMessage(ns_log::Warning, "编译失败\n");
            return false;
        }
    };
}